package calc

import org.junit.Assert._

import org.junit.Before
import org.junit.Test

class CalculatorTest {

  @Test
  def testCalculateIntegers() {
    val ints = new Calculator with IntegerArithmetic

    assertEquals(3, ints.calculate("1+2"))
    assertEquals(11, ints.calculate("1+2*3+4"))
    assertEquals(5, ints.calculate("(1+2*3+4)/2"))
  }

  @Test
  def testCalculateDoubles() {
    val doubles = new Calculator with DoubleArithmetic

    assertEquals(3.0, doubles.calculate("1+2"), 0.1)
    assertEquals(4.0, doubles.calculate("1.3+2.7"), 0.1)
    assertEquals(11.0, doubles.calculate("1+2*3+4"), 0.1)
    assertEquals(0.5, doubles.calculate("1/2"), 0.1)
    assertEquals(-13.0, doubles.calculate("1+2*3-4*5"), 0.1)
    assertEquals(-1.0, doubles.calculate("(1+2)*3-4*5/2"), 0.1)
    assertEquals(13.0, doubles.calculate("-(1-2)+3*4"), 0.1)
    assertEquals(13.0, doubles.calculate("-((-(-1)-2))+(3*(4))"), 0.1)
    assertEquals(8, doubles.calculate("-(-(5+1)/3)*4"), 0.1)
  }

  @Test
  def testCalculateHexadecimals() {
    val hexs = new Calculator with HexArithmetic

    assertEquals(new Hex(0x1F + 0x2E), hexs.calculate("1f+2e"))
    assertEquals(new Hex(0xA), hexs.calculate("1+2+3+4"))
  }

  @Test
  def testTrigonometricFunctions() {
    val trigs = new FunctionCalculator with DoubleArithmetic with TrigonometricFunctions with OtherFunctions

    assertEquals((1.0 + Math.sin(1)) * 3 + Math.tan(0.4), trigs.calculate("(1+sin(1))*3+tan(0.2+0.2)"), 0.1)
    assertEquals(Math.exp(1) + Math.tan(0.4), trigs.calculate("exp(1)+tan(0.4)"), 0.1)
  }

  @Test
  def testCalculateHexFunc() {
    val hexsFunc = new FunctionCalculator with HexArithmetic with NotVeryUsefulFunctions

    assertEquals(new Hex(3), hexsFunc.calculate("1+dostuff(1)"))
  }

  @Test
  def testTokenize() {
    val ints = new Calculator with IntegerArithmetic
    val _1 = Number("1")
    val _2 = Number("2")
    val _3 = Number("3")
    val _4 = Number("4")
    val _5 = Number("5")
    val _p = Plus()
    val _m = Minus()
    val _um = UMinus()
    val _mul = Multiply()
    val _div = Divide()
    val _o = OpenParen()
    val _c = CloseParen()
    assertEquals(List(_1, _p, _2), ints.tokenize("1+2".toList))
    assertEquals(List(_p, _m, _mul, _div), ints.tokenize("+ - * /".toList))
    assertEquals(List(_o, _c), ints.tokenize("()".toList))
    assertEquals(List(_um, _o, _um, Number("12345"), _c), ints.tokenize("-(-12345)".toList))
  }

  @Test
  def testInfix2postfix() {
    val ints = new Calculator with IntegerArithmetic
    val _1 = Number[ints.T]("1")
    val _2 = Number[ints.T]("2")
    val _3 = Number[ints.T]("3")
    val _4 = Number[ints.T]("4")
    val _5 = Number[ints.T]("5")
    val _p = Plus[ints.T]()
    val _m = Minus[ints.T]()
    val _um = UMinus[ints.T]()
    val _mul = Multiply[ints.T]()
    val _div = Divide[ints.T]()
    val _o = OpenParen[ints.T]()
    val _c = CloseParen[ints.T]()

    //1+2 -> 12+
    assertEquals(List(_1, _2, _p), ints.infix2postfix(List(_1, _p, _2)))

    //-1+2 -> 1-2+
    assertEquals(List(_1, _um, _2, _p), ints.infix2postfix(List(_um, _1, _p, _2)))

    //-1*2 -> 1-2*
    assertEquals(List(_1, _um, _2, _mul), ints.infix2postfix(List(_um, _1, _mul, _2)))

    //-(1-2)+3*4 -> 12--34*+
    assertEquals(List(_1, _2, _m, _um, _3, _4, _mul, _p), ints.infix2postfix(
      List(_um, _o, _1, _m, _2, _c, _p, _3, _mul, _4)))

    //1+2*3 -> 123*+
    assertEquals(List(_1, _2, _3, _mul, _p), ints.infix2postfix(List(_1, _p, _2, _mul, _3)))

    //1-2+3 -> 12-3+
    assertEquals(List(_1, _2, _m, _3, _p), ints.infix2postfix(List(_1, _m, _2, _p, _3)))

    //1-2*3-4/5*2 -> 123*-45/2*-
    assertEquals(List(_1, _2, _3, _mul, _m, _4, _5, _div, _2, _mul, _m),
      ints.infix2postfix(List(_1, _m, _2, _mul, _3, _m, _4, _div, _5, _mul, _2)))

    //1*2+3/4-5*1 -> 12*34/+51*-
    assertEquals(List(_1, _2, _mul, _3, _4, _div, _p, _5, _1, _mul, _m),
      ints.infix2postfix(List(_1, _mul, _2, _p, _3, _div, _4, _m, _5, _mul, _1)))

    //(1+2)*3 -> 12+3*
    assertEquals(List(_1, _2, _p, _3, _mul), ints.infix2postfix(List(_o, _1, _p, _2, _c, _mul, _3)))

    //(1+2-3)*4 -> 12+3-4*
    assertEquals(List(_1, _2, _p, _3, _m, _4, _mul), ints.infix2postfix(List(_o, _1, _p, _2, _m, _3, _c, _mul, _4)))

    //(1*2)/(3*4) -> 12*34*/
    assertEquals(List(_1, _2, _mul, _3, _4, _mul, _div), ints.infix2postfix(List(_o, _1, _mul, _2, _c, _div, _o, _3, _mul, _4, _c)))
  }
}
